# Copyright (C) 2007 Insecure.Com LLC.
#
# Authors: Guilherme Polo <ggpolo@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 
# USA

import gtk
import gobject

from Base import tl_colors
from Base import check_instance
from Base import gdkc_to_cairoc

 
####
## TimeLine arrows 
LEFT_ARROW, RIGHT_ARROW, DOWN_ARROW, UP_ARROW = (-1, 1, -2, -3)
DATE_ARROWS = (LEFT_ARROW, RIGHT_ARROW)
ZOOM_ARROWS = (DOWN_ARROW, UP_ARROW)
ARROWS = DATE_ARROWS + ZOOM_ARROWS

class Error(Exception):
    pass

class BadArrow(Error):
    pass


class TL_Arrow(gtk.Widget):
    __gsignals__ = { 'activate': (gobject.SIGNAL_RUN_LAST,
                                  gobject.TYPE_NONE,
                                  ()),
                     'sensitive': (gobject.SIGNAL_RUN_LAST,
                                   gobject.TYPE_NONE,
                                   ()),
                     'clicked': (gobject.SIGNAL_RUN_LAST,
                                 gobject.TYPE_NONE,
                                 ())
    }

    
    def __init__(self, arrow, tldaddy):
        self.tl = check_instance(tldaddy)  # TimeLine instance
        
        gtk.Widget.__init__(self)
        
        self.__focus = False
        
        self.arrow_size = (15, 8.55)  # should be good for the arrow
        self.arrow = arrow                 # arrow direction
        self.draw_border = False      # Draw border now (no!)
        self.hovering = False            # Nothing being hovered
        self.dy = 2                            # Distance from top
        
        self.inactive_color = gdkc_to_cairoc(
                                self.style.fg[gtk.STATE_INSENSITIVE].red,
                                self.style.fg[gtk.STATE_INSENSITIVE].green,
                                self.style.fg[gtk.STATE_INSENSITIVE].blue,
                                65535)
    
    
    def get_dy(self):
        """
        Gets distance from top
        """
        return self.__dy
    
    
    def set_dy(self, dy):
        """
        Sets a distance from top.
        """
        self.__dy = dy


    def get_hover(self):
        """
        Get hover state.
        """
        return self.__hover

    
    def get_arrow(self):
        """
        Gets the arrow to be draw.
        """
        return self.__arrow

    
    def set_arrow(self, arrow):
        """
        Sets the arrow to be draw.
        """
        if arrow not in ARROWS:
            raise BadArrow(arrow)
        self.__arrow = arrow

    
    def get_arrow_size(self):
        """
        Gets arrow size.
        """
        return self.__arrow_size

    
    def set_arrow_size(self, dim):
        """
        Sets arrow width, height.
        """
        self.__arrow_size = dim


    def date_change(self):
        """
        Changes current date.
        """
        if self.arrow == RIGHT_ARROW:
            self.tl.go_next_date()
        else:
            self.tl.go_prev_date()


    def change_current_view(self):
        """
        Change current zoom.
        """
        if self.arrow == DOWN_ARROW:
            self.tl.zoom_in()
        else:
            self.tl.zoom_out()


    def do_realize(self):                           
        self.set_flags(self.flags() | gtk.REALIZED | gtk.CAN_FOCUS)
        
        # create a new gtk.gdk.Window so we can draw on it ;)
        # also, lets make it possible to receive exposure events by 
        # setting the event_mask
        self.window = gtk.gdk.Window(self.get_parent_window(), 
                                     width=self.allocation.width,
                                     height=self.allocation.height, 
                                     window_type=gtk.gdk.WINDOW_CHILD,
                                     wclass=gtk.gdk.INPUT_OUTPUT,
                                     event_mask=self.get_events() | 
                                                gtk.gdk.EXPOSURE_MASK |
                                                gtk.gdk.BUTTON_PRESS_MASK | 
                                                gtk.gdk.BUTTON_RELEASE_MASK |
                                                gtk.gdk.POINTER_MOTION_MASK |
                                                gtk.gdk.ENTER_NOTIFY_MASK | 
                                                gtk.gdk.LEAVE_NOTIFY_MASK)
        
        # associate the gtk.gdk.Window with ourselves, Gtk+ needs a 
        # reference between the widget and the gdk window
        self.window.set_user_data(self)
        
        # attach the style to the gdk.Window, a style contains colors and
        # graphic contexts used for drawing                      
        self.style.attach(self.window)
        
        # the default color of the background should be what
        # theme engine tells us.
        self.style.set_background(self.window, gtk.STATE_NORMAL)
        
        self.window.move_resize(*self.allocation)
        
        # lets make it possible to receive 'activate'
        self.set_activate_signal('activate')


    def do_unrealize(self):
        """
        Release resources when requested.
        """
        self.window.set_user_data(None)


    def do_size_request(self, requisition):
        """
        This will be called when 'size-request' is emitted
        """
        arrow_size = self.arrow_size
        
        requisition.width = arrow_size[0] + 4
        requisition.height = arrow_size[1] + 4# * 2


    def do_size_allocate(self, allocation):
        """
        If we are realized, move and resize the window to the requested
        coords/positions.
        """
        self.allocation = allocation
        
        if self.flags() & gtk.REALIZED:
            self.window.move_resize(*allocation)


    def do_button_press_event(self, event):
        """
        Mouse button pressed.
        """
        self.grab_focus()
        self.emit('clicked')
    
    
    def do_activate(self):
        """
        Widget activated.
        """
        self.hovering = True
        self.queue_draw()
        
        gobject.timeout_add(100, self.__activate_finish)
    
    
    def do_button_release_event(self, event):
        """
        Mouse button released.
        """
        if self.hovering:
            if self.arrow in DATE_ARROWS:
                self.date_change()
            else:
                self.change_current_view()                
        
        self.queue_draw()
    
    
    def do_enter_notify_event(self, event):
        """
        Mouse entered the widget.
        """
        self.hovering = True
        self.queue_draw()
        
        
    def do_leave_notify_event(self, event):
        """
        Mouse left the widget.
        """
        self.hovering = False
        self.queue_draw()
    
    
    def do_focus_in_event(self, event):
        """
        Keyboard entered the widget
        """
        self.__focus = True
        self.queue_draw()
    
    
    def do_focus_out_event(self, event):
        """
        Keyboard left the widget
        """
        self.__focus = False
        self.queue_draw()
    
    
    def do_expose_event(self, event):
        """
        Draw upon request.
        """
        cr = self.window.cairo_create()
        cr.rectangle(*event.area)
        cr.clip()
        
        cr.set_line_width(0.5)
        if self.tl.draw_border and self.__focus:
           self.__draw_border_box(cr)
        
        self.__draw_arrow(cr, self.arrow)


    ##
    # Private methods follows.

    def __activate_finish(self):
        """
        Handles signal 'activate'.
        """
        if self.arrow in DATE_ARROWS:
            self.date_change()
        else: 
            self.change_current_view()
        
        self.hovering = False
        self.queue_draw()
        return False


    def __set_hover(self, hover):
        """
        Set hover state.
        """
        self.__hover = hover


    def __draw_border_box(self, cr):
        """
        Draws a rectangle box, shown when widget is focused.
        """
        cr.save()
        cr.set_line_width(0.2)
        
        cr.set_dash([1, 1], 0)
        cr.new_sub_path()
        cr.rectangle(0, 0, self.allocation[2], self.allocation[3])
        
        cr.close_path()
        cr.stroke()
        cr.restore() 


    def __draw_arrow(self, cr, arrow):
        """
        Draws an arrow.
        """
        
        # left = -1
        # right = 1
        # down = -2
        # up = -3
        
        arrow_size = self.arrow_size
        start_x = (arrow == LEFT_ARROW) and arrow_size[0]+2 or 2
        
        cr.save()
        
        cr.new_path()
        
        if arrow in DATE_ARROWS:
            cr.move_to(start_x, arrow_size[1] * 2 + 2)
            cr.rel_line_to(arrow * arrow_size[0], - arrow_size[1])
            cr.rel_line_to((arrow * -1) * arrow_size[0], - arrow_size[1])
        elif arrow in ZOOM_ARROWS:
            cr.move_to(start_x, (arrow == -3) and arrow_size[1]+2 or 2)
            cr.rel_line_to(arrow_size[0], 0)
            cr.rel_line_to(-1 * arrow_size[0]/2, 
                           ((arrow == -3) and (-1) or 1) * arrow_size[1])
        
        cr.close_path()
        
        
        if self.state == gtk.STATE_INSENSITIVE:
            color = self.inactive_color
        else:
            color = tl_colors["arrow_bg"]
            
            if self.hovering:
                cr.set_line_width(2)    
            
        cr.set_source_rgba(*color)    
        cr.fill_preserve()
        cr.stroke()
        cr.restore()


    # Properties
    arrow = property(get_arrow, set_arrow)
    arrow_size = property(get_arrow_size, set_arrow_size)
    hovering = property(get_hover, __set_hover)
    dy = property(get_dy, set_dy)


gobject.type_register(TL_Arrow) 
